/*!
 * drag.js
 * Date: 2013-12-06
 * https://github.com/aui/artDialog
 * (c) 2009-2014 TangBin, http://www.planeArt.cn
 *
 * This is licensed under the GNU LGPL, version 2.1 or later.
 * For details, see: http://www.gnu.org/licenses/lgpl-2.1.html
 */
define(function(require) {

    var $ = require('jquery');


    var $window = $(window);
    var $document = $(document);
    var isTouch = 'createTouch' in document;
    var html = document.documentElement;
    var isIE6 = !('minWidth' in html.style);
    var isLosecapture = !isIE6 && 'onlosecapture' in html;
    var isSetCapture = 'setCapture' in html;


    var types = {
        start: isTouch ? 'touchstart' : 'mousedown',
        over: isTouch ? 'touchmove' : 'mousemove',
        end: isTouch ? 'touchend' : 'mouseup'
    };


    var getEvent = isTouch ? function(event) {
        if (!event.touches) {
            event = event.originalEvent.touches.item(0);
        }
        return event;
    } : function(event) {
        return event;
    };


    var DragEvent = function() {
        this.start = $.proxy(this.start, this);
        this.over = $.proxy(this.over, this);
        this.end = $.proxy(this.end, this);
        this.onstart = this.onover = this.onend = $.noop;
    };

    DragEvent.types = types;

    DragEvent.prototype = {

        start: function(event) {
            event = this.startFix(event);

            $document
                .on(types.over, this.over)
                .on(types.end, this.end);

            this.onstart(event);
            return false;
        },

        over: function(event) {
            event = this.overFix(event);
            this.onover(event);
            return false;
        },

        end: function(event) {
            event = this.endFix(event);

            $document
                .off(types.over, this.over)
                .off(types.end, this.end);

            this.onend(event);
            return false;
        },

        startFix: function(event) {
            event = getEvent(event);

            this.target = $(event.target);
            this.selectstart = function() {
                return false;
            };

            $document
                .on('selectstart', this.selectstart)
                .on('dblclick', this.end);

            if (isLosecapture) {
                this.target.on('losecapture', this.end);
            } else {
                $window.on('blur', this.end);
            }

            if (isSetCapture) {
                this.target[0].setCapture();
            }

            return event;
        },

        overFix: function(event) {
            event = getEvent(event);
            return event;
        },

        endFix: function(event) {
            event = getEvent(event);

            $document
                .off('selectstart', this.selectstart)
                .off('dblclick', this.end);

            if (isLosecapture) {
                this.target.off('losecapture', this.end);
            } else {
                $window.off('blur', this.end);
            }

            if (isSetCapture) {
                this.target[0].releaseCapture();
            }

            return event;
        }

    };


    /**
     * 启动拖拽
     * @param   {HTMLElement}   被拖拽的元素
     * @param   {Event} 触发拖拽的事件对象。可选，若无则监听 elem 的按下事件启动
     */
    DragEvent.create = function(elem, event) {
        var $elem = $(elem);
        var dragEvent = new DragEvent();
        var startType = DragEvent.types.start;
        var noop = function() {};
        var className = elem.className
            .replace(/^\s|\s.*/g, '') + '-drag-start';

        var minX;
        var minY;
        var maxX;
        var maxY;

        var api = {
            onstart: noop,
            onover: noop,
            onend: noop,
            off: function() {
                $elem.off(startType, dragEvent.start);
            }
        };


        dragEvent.onstart = function(event) {
            var isFixed = $elem.css('position') === 'fixed';
            var dl = $document.scrollLeft();
            var dt = $document.scrollTop();
            var w = $elem.width();
            var h = $elem.height();

            minX = 0;
            minY = 0;
            maxX = isFixed ? $window.width() - w + minX : $document.width() - w;
            maxY = isFixed ? $window.height() - h + minY : $document.height() - h;

            var offset = $elem.offset();
            var left = this.startLeft = isFixed ? offset.left - dl : offset.left;
            var top = this.startTop = isFixed ? offset.top - dt : offset.top;

            this.clientX = event.clientX;
            this.clientY = event.clientY;

            $elem.addClass(className);
            api.onstart.call(elem, event, left, top);
        };


        dragEvent.onover = function(event) {
            var left = event.clientX - this.clientX + this.startLeft;
            var top = event.clientY - this.clientY + this.startTop;
            var style = $elem[0].style;

            left = Math.max(minX, Math.min(maxX, left));
            top = Math.max(minY, Math.min(maxY, top));

            style.left = left + 'px';
            style.top = top + 'px';

            api.onover.call(elem, event, left, top);
        };


        dragEvent.onend = function(event) {
            var position = $elem.position();
            var left = position.left;
            var top = position.top;
            $elem.removeClass(className);
            api.onend.call(elem, event, left, top);
        };


        dragEvent.off = function() {
            $elem.off(startType, dragEvent.start);
        };


        if (event) {
            dragEvent.start(event);
        } else {
            $elem.on(startType, dragEvent.start);
        }

        return api;
    };

    return DragEvent;

});